﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WFL.Windows.Forms
{
    public class FormBase : Form
    {

        #region 状态字段属性

        private int _state;

        protected bool GetState(int bit)
        {
            return (_state & bit) == bit;
        }

        protected void SetState(int bit, bool flag)
        {
            _state = flag ? _state | bit : _state & ~bit;
        }

        //抑制LocationChanged、SizeChanged、ClientSizeChanged事件
        private bool SuppressBoundsEvent
        {
            get { return GetState(0x01); }
            set { SetState(0x01, value); }
        }

        private bool SettingClientSize
        {
            get { return GetState(0x02); }
            set { SetState(0x02, value); }
        }

        private int WmWindowPosChangedLoop = 0;

        #endregion


        #region 非客户区区域

        private int _titleHeight;
        public int TitleHeight
        {
            get { return _titleHeight; }
            set
            {
                if (value < 0)
                {
                    value = 0;
                }
                if (_titleHeight != value)
                {
                    _titleHeight = value;
                    UpdateSizeBaseOnClientSize();
                }
            }
        }

        private Padding _borderThickness;
        public Padding BorderThickness
        {
            get { return _borderThickness; }
            set
            {
                if (value.Left < 1)
                {
                    value.Left = 1;
                }
                if (value.Top < 1)
                {
                    value.Top = 1;
                }
                if (value.Right < 1)
                {
                    value.Right = 1;
                }
                if (value.Bottom < 1)
                {
                    value.Bottom = 1;
                }
                if (_borderThickness != value)
                {
                    _borderThickness = value;
                    UpdateSizeBaseOnClientSize();
                }
            }
        }

        protected int NcLeftWidth
        {
            get { return _borderThickness.Left; }
        }

        protected int NcRightWidth
        {
            get { return _borderThickness.Right; }
        }

        protected int NcWidth
        {
            get { return _borderThickness.Left + _borderThickness.Right; }
        }

        protected int NcTopBorderHeight
        {
            get { return _borderThickness.Top; }
        }

        protected int NcTopHeight
        {
            get { return _borderThickness.Top + _titleHeight; }
        }

        protected int NcBottomHeight
        {
            get { return _borderThickness.Bottom; }
        }

        protected int NcHeight
        {
            get { return _borderThickness.Top + _titleHeight + _borderThickness.Bottom; }
        }

        #endregion


        public FormBase()
        {
            _titleHeight = 22;
            _borderThickness = new Padding(8);
        }


        #region private方法

        private void UpdateSizeBaseOnClientSize()
        {
            Size size = SizeFromClientSize(ClientSize);
            Size = size;
        }

        private Size ClientSizeFromStandardSize(Size size)
        {
            //获取标准非客户区的大小
            Size nonclientSize = base.SizeFromClientSize(Size.Empty);
            //正在设置的客户区大小
            Size clientSize = new Size(size.Width - nonclientSize.Width, size.Height - nonclientSize.Height);
            return clientSize;
        }

        private Size AdjustSizeFromStandardSize(Size size)
        {
            //获取当前标准窗口大小对应的客户区大小
            Size clientSize = ClientSizeFromStandardSize(size);
            //获取容纳当前客户区大小的自定义窗口大小
            size = SizeFromClientSize(clientSize);
            return size;
        }

        #endregion


        #region protected方法

        protected virtual Rectangle GetMaximizedBounds()
        {
            Rectangle maximizedBounds = Screen.GetWorkingArea(this);
            maximizedBounds.X -= NcLeftWidth;
            maximizedBounds.Y -= NcTopBorderHeight;
            maximizedBounds.Width += NcWidth;
            maximizedBounds.Height += NcTopBorderHeight + NcBottomHeight;
            return maximizedBounds;
        }

        protected virtual Region GetWindowRegion()
        {
            return null;
        }

        protected virtual bool WmNcPaint(Graphics graphics)
        {
            return false;
        }

        protected virtual int WmNcHitTest(Point point)
        {
            if (WindowState == FormWindowState.Maximized)
            {
                if (point.X > NcLeftWidth
                    && point.X < Width - NcRightWidth)
                {
                    if (point.Y > NcTopBorderHeight
                        && point.Y <= NcTopHeight)
                    {
                        return Header.Winuser.HTCAPTION;
                    }
                    if (point.Y < Height - NcBottomHeight)
                    {
                        return Header.Winuser.HTCLIENT;
                    }
                }
                return Header.Winuser.HTBORDER;
            }
            Size dragSize = SystemInformation.DragSize;
            if (point.Y >= Height - NcBottomHeight)
            {
                if (point.X >= Width - dragSize.Width)
                {
                    return Header.Winuser.HTBOTTOMRIGHT;
                }
                if (point.X > dragSize.Width)
                {
                    return Header.Winuser.HTBOTTOM;
                }
                if (point.X <= dragSize.Width)
                {
                    return Header.Winuser.HTBOTTOMLEFT;
                }
            }
            else if (point.Y > NcTopHeight)
            {
                if (point.X >= Width - NcRightWidth)
                {
                    return Header.Winuser.HTRIGHT;
                }
                if (point.X > NcLeftWidth)
                {
                    return Header.Winuser.HTCLIENT;
                }
                if (point.X <= NcLeftWidth)
                {
                    return Header.Winuser.HTLEFT;
                }
            }
            else if (point.Y > NcTopBorderHeight)
            {
                if (point.X >= Width - NcRightWidth)
                {
                    return Header.Winuser.HTRIGHT;
                }
                if (point.X > NcLeftWidth)
                {
                    return Header.Winuser.HTCAPTION;
                }
                if (point.X <= NcLeftWidth)
                {
                    return Header.Winuser.HTLEFT;
                }
            }
            else if (point.Y <= NcTopBorderHeight)
            {
                if (point.X >= Width - dragSize.Width)
                {
                    return Header.Winuser.HTTOPRIGHT;
                }
                if (point.X > dragSize.Width)
                {
                    return Header.Winuser.HTTOP;
                }
                if (point.X <= dragSize.Width)
                {
                    return Header.Winuser.HTTOPLEFT;
                }
            }
            return Header.Winuser.HTNOWHERE;
        }

        protected virtual bool WmNcActivate(Graphics graphics, bool active)
        {
            return false;
        }

        #endregion


        #region 重写基类属性、方法

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                if (cp.Width != Width
                        || cp.Height != Height)
                {
                    Size size = SizeFromClientSize(ClientSize);
                    cp.Width = size.Width;
                    cp.Height = size.Height;
                }
                return cp;
            }
        }

        protected override void OnLocationChanged(EventArgs e)
        {
            if (!SuppressBoundsEvent)
            {
                base.OnLocationChanged(e);
            }
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            if (!SuppressBoundsEvent)
            {
                base.OnSizeChanged(e);
            }
        }

        protected override void OnClientSizeChanged(EventArgs e)
        {
            if (!SuppressBoundsEvent)
            {
                base.OnClientSizeChanged(e);
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            if (Region != null)
            {
                Region.Dispose();
            }
            Region = GetWindowRegion();
        }

        protected override Size SizeFromClientSize(Size clientSize)
        {
            return Size.Add(clientSize, new Size(NcWidth, NcHeight));
        }

        protected override void SetClientSizeCore(int x, int y)
        {
            SettingClientSize = true;
            SuppressBoundsEvent = true;
            base.SetClientSizeCore(x, y);
            this.UpdateBounds(Left, Top, Width, Height, x, y);
            SuppressBoundsEvent = false;
            OnClientSizeChanged(EventArgs.Empty);
            SettingClientSize = false;
        }

        protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
        {
            bool flag = (specified & BoundsSpecified.Size) != BoundsSpecified.None
                        && !IsHandleCreated;
            if (flag)
            {
                SuppressBoundsEvent = true;
            }
            //原来的位置和大小
            Point location0 = Location;
            Size size0 = Size;
            Size clientSize0 = ClientSize;
            //目标大小
            int width1 = width, height1 = height;
            bool flag1 = (specified & BoundsSpecified.Size) != BoundsSpecified.None
                        && WmWindowPosChangedLoop > 0;
            if (flag1 || SettingClientSize)
            {
                Size size = AdjustSizeFromStandardSize(new Size(width, height));
                width1 = size.Width;
                height1 = size.Height;
            }
            base.SetBoundsCore(x, y, width1, height1, specified);
            if (flag)
            {
                //设置后的客户区大小
                Size clientSize = new Size(Width - NcWidth, Height - NcHeight);
                if (clientSize != this.ClientSize)
                {
                    this.UpdateBounds(Left, Top, Width, Height, clientSize.Width, clientSize.Height);
                }
                SuppressBoundsEvent = false;
                if ((((specified & BoundsSpecified.X) == BoundsSpecified.X) && Left != location0.X)
                    || (((specified & BoundsSpecified.Y) == BoundsSpecified.Y) && Top != location0.Y))
                {
                    OnLocationChanged(EventArgs.Empty);
                }
                if (Width != size0.Width
                    || Height != size0.Height
                    || clientSize.Width != clientSize0.Width
                    || clientSize.Height != clientSize0.Height)
                {
                    OnSizeChanged(EventArgs.Empty);
                    OnClientSizeChanged(EventArgs.Empty);
                }
                if (SettingClientSize)
                {
                    SuppressBoundsEvent = true;
                }
            }
        }

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                default:
                    base.WndProc(ref m);
                    break;
                case Header.Winuser.WM_GETMINMAXINFO:
                    WmGetMinMaxInfo(ref m);
                    break;
                case Header.Winuser.WM_WINDOWPOSCHANGED:
                    WmWindowPosChanged(ref m);
                    break;
                case Header.Winuser.WM_NCCALCSIZE:
                    WmNcCalcSize(ref m);
                    break;
                case Header.Winuser.WM_NCPAINT:
                    WmNcPaint(ref m);
                    break;
                case Header.Winuser.WM_NCHITTEST:
                    WmNcHitTest(ref m);
                    break;
                case Header.Winuser.WM_NCACTIVATE:
                    WmNcActivate(ref m);
                    break;
            }
        }

        #endregion


        #region 消息处理

        private void WmGetMinMaxInfo(ref Message m)
        {
            MINMAXINFO minMaxInfo = (MINMAXINFO)m.GetLParam(typeof(MINMAXINFO));
            Rectangle maximizedBounds = GetMaximizedBounds();
            minMaxInfo.ptMaxPosition.x = maximizedBounds.X;
            minMaxInfo.ptMaxPosition.y = maximizedBounds.Y;
            minMaxInfo.ptMaxSize.x = maximizedBounds.Width;
            minMaxInfo.ptMaxSize.y = maximizedBounds.Height;
            minMaxInfo.ptMaxTrackSize = minMaxInfo.ptMaxSize;
            if (!MaximumSize.IsEmpty)
            {
                minMaxInfo.ptMaxTrackSize.x = Math.Min(minMaxInfo.ptMaxTrackSize.x, MaximumSize.Width);
                minMaxInfo.ptMaxTrackSize.y = Math.Min(minMaxInfo.ptMaxTrackSize.y, MaximumSize.Height);
            }
            minMaxInfo.ptMinTrackSize.x = Math.Max(minMaxInfo.ptMinTrackSize.x, NcWidth);
            minMaxInfo.ptMinTrackSize.y = Math.Max(minMaxInfo.ptMinTrackSize.y, NcHeight);
            if (!MinimumSize.IsEmpty)
            {
                minMaxInfo.ptMinTrackSize.x = Math.Max(minMaxInfo.ptMinTrackSize.x, MinimumSize.Width);
                minMaxInfo.ptMinTrackSize.y = Math.Max(minMaxInfo.ptMinTrackSize.y, MinimumSize.Height);
            }
            Marshal.StructureToPtr(minMaxInfo, m.LParam, false);
            m.Result = IntPtr.Zero;
        }

        private void WmWindowPosChanged(ref Message m)
        {
            WmWindowPosChangedLoop++;
            base.WndProc(ref m);
            WmWindowPosChangedLoop--;
        }

        private void WmNcCalcSize(ref Message m)
        {
            if (m.WParam.ToInt32() == 0)
            {
                RECT rect = (RECT)m.GetLParam(typeof(RECT));
                rect.Left += NcLeftWidth;
                rect.Top += NcTopHeight;
                rect.Right -= NcRightWidth;
                rect.Bottom -= NcBottomHeight;
                Marshal.StructureToPtr(rect, m.LParam, false);
                m.Result = IntPtr.Zero;
            }
            else
            {
                NCCALCSIZE_PARAMS np = (NCCALCSIZE_PARAMS)m.GetLParam(typeof(NCCALCSIZE_PARAMS));
                WINDOWPLACEMENT placement = new WINDOWPLACEMENT() { length = (uint)Marshal.SizeOf(typeof(WINDOWPLACEMENT)) };
                API.User32.GetWindowPlacement(Handle, ref placement);
                switch (placement.showCmd)
                {
                    default:
                        np.Rgrc[0].Left += NcLeftWidth;
                        np.Rgrc[0].Top += NcTopHeight;
                        np.Rgrc[0].Right -= NcRightWidth;
                        np.Rgrc[0].Bottom -= NcBottomHeight;
                        break;
                    case Header.Winuser.SW_SHOWMINIMIZED:
                    case Header.Winuser.SW_MINIMIZE:
                    case Header.Winuser.SW_SHOWMINNOACTIVE:
                        base.WndProc(ref m);
                        break;
                }
                Marshal.StructureToPtr(np, m.LParam, false);
                m.Result = IntPtr.Zero;
            }
        }

        private void WmNcPaint(ref Message m)
        {
            IntPtr hDC = API.User32.GetWindowDC(Handle);
            Graphics graphics = Graphics.FromHdc(hDC);
            try
            {
                graphics.SetClip(new Rectangle(0, 0, Width, NcTopHeight));
                graphics.SetClip(new Rectangle(0, NcTopHeight, NcLeftWidth, Height - NcHeight), CombineMode.Union);
                graphics.SetClip(new Rectangle(Width - NcRightWidth, NcTopHeight, NcRightWidth, Height - NcHeight), CombineMode.Union);
                graphics.SetClip(new Rectangle(0, Height - NcBottomHeight, Width, NcBottomHeight), CombineMode.Union);
                if (!WmNcPaint(graphics))
                {
                    base.WndProc(ref m);
                }
            }
            finally
            {
                graphics.Dispose();
                API.User32.ReleaseDC(Handle, hDC);
            }
        }

        private void WmNcHitTest(ref Message m)
        {
            Point point = new Point()
            {
                X = Header.Windowsx.GET_X_LPARAM(m.LParam),
                Y = Header.Windowsx.GET_Y_LPARAM(m.LParam)
            };
            point.Offset(-Left, -Top);
            int hit = WmNcHitTest(point);
            if (hit != Header.Winuser.HTNOWHERE)
            {
                m.Result = new IntPtr(hit);
            }
            else
            {
                base.WndProc(ref m);
            }
        }

        private void WmNcActivate(ref Message m)
        {
            bool active = m.WParam.ToInt32() > 0;
            IntPtr hDC = API.User32.GetWindowDC(Handle);
            Graphics graphics = Graphics.FromHdc(hDC);
            try
            {
                graphics.SetClip(new Rectangle(0, 0, Width, NcTopHeight));
                graphics.SetClip(new Rectangle(0, NcTopHeight, NcLeftWidth, Height - NcHeight), CombineMode.Union);
                graphics.SetClip(new Rectangle(Width - NcRightWidth, NcTopHeight, NcRightWidth, Height - NcHeight), CombineMode.Union);
                graphics.SetClip(new Rectangle(0, Height - NcBottomHeight, Width, NcBottomHeight), CombineMode.Union);
                if (WmNcActivate(graphics, active))
                {
                    m.Result = (IntPtr)1;
                }
                else
                {
                    base.WndProc(ref m);
                }
            }
            finally
            {
                graphics.Dispose();
                API.User32.ReleaseDC(Handle, hDC);
            }
        }

        #endregion



    }
}
